//
//  IKImageView.m
//  Seams
//
//  Created by Charles Osmer on 4/2/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "SMImageView.h"
#import "CGImage-SMExtras.h"

@implementation IKImageView

@synthesize showSeams;

- (void)initCommon
{
    _image = SMImage_Initializer;
    _seamGraph = SMForwardGraph_Initializer;

    showSeams = NO;

    _dirtyImage = _dirtyGraph = NO;
}

- (id)initWithFrame:(NSRect)frame
{
    if ([super initWithFrame:frame]) {
        [self initCommon];
    }

    return self;
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if ([super initWithCoder:decoder]) {
        [self initCommon];
    }

    return self;
}

- (void)setShowSeams:(BOOL)flag
{
    if (flag == self.showSeams)
        return;

    showSeams = flag;
    _dirtyImage = YES;

    [self setNeedsDisplay:YES];
}

- (void)updateSeamGraph
{
    if (_dirtyGraph == NO)
        return;
    
    _dirtyGraph = NO;

    if (_seamGraph.image == NULL)
        SMForwardGraph_Alloc(&_seamGraph, &_image);
    else
        SMForwardGraph_ResetGraph(&_seamGraph);

    const clock_t start = clock();
    SMForwardGraph_CalculateSeams(&_seamGraph, _image.width);
    const clock_t end = clock();
    NSLog(@"Carved %ix%i image in %ims", _image.width, _image.height, (end-start)*1000/CLOCKS_PER_SEC);

    NSWindow *window = [self window];
    [window setContentMinSize:NSMakeSize(_seamGraph.image->width-_seamGraph.nSeams, _seamGraph.image->height)];
    [window setContentMaxSize:NSMakeSize(_seamGraph.image->width, _seamGraph.image->height)];
    [window setContentSize:[window contentMaxSize]];
}

- (void)setImageWithContentsOfFile:(NSString *)filepath
{
    NSURL *url = [NSURL fileURLWithPath:filepath];

    CGImageSourceRef sourceRef = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
    if (!sourceRef) {
        NSLog(@"Can't create image source - %@", url);
        return;
    }

    CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
    CFRelease(sourceRef);
    
    const int width = CGImageGetWidth(imageRef);
    const int height = CGImageGetHeight(imageRef);

    SMImage_Alloc(&_image, 3, width, height);
    SMImagePlane rPlane = _image.planes[0];
    SMImagePlane gPlane = _image.planes[1];
    SMImagePlane bPlane = _image.planes[2];

    const unsigned int *rgbData = CGImageGetRGBData(imageRef);
    for (int y=0; y < height; y++) {
        for (int x=0; x < width; x++) {
            const unsigned int rgbPixel = rgbData[y*width+x];

            const unsigned int b = (rgbPixel >>  8) & 255;
            const unsigned int g = (rgbPixel >> 16) & 255;
            const unsigned int r = (rgbPixel >> 24) & 255;

            rPlane[y][x] = r;
            gPlane[y][x] = g;
            bPlane[y][x] = b;
        }
    }

    // Seam graph and image need to be updated with new data.
    _dirtyGraph = _dirtyImage = YES;

    [self setNeedsDisplay:YES];
}

- (NSImageRep *)bitmapRepForSize:(NSSize)size rect:(NSRect)frame
{
    if (_image.nPlanes == 0)
        return nil;

    SMImage image = SMImage_Initializer;

    if (!self.showSeams) {
        const int maskLevel = (frame.size.width >= _image.width ? 0 : _image.width - frame.size.width);
        image = SMForwardGraph_CarveImage(&_seamGraph, maskLevel);
    }
    else
        SMImage_Copy(&image, &_image);

    SMImage *workingImage = &image;

    const int width = workingImage->width;
    const int height = workingImage->height;

    const SMImagePlane rPlane = workingImage->planes[(workingImage->nPlanes == 1 ? 0 : 0)];
    const SMImagePlane gPlane = workingImage->planes[(workingImage->nPlanes == 1 ? 0 : 1)];
    const SMImagePlane bPlane = workingImage->planes[(workingImage->nPlanes == 1 ? 0 : 2)];

    unsigned char *rgbData = (unsigned char *)calloc(width*height, sizeof(unsigned int));

    for (int y=0; y < workingImage->height; y++) {
        for (int x=0; x < workingImage->width; x++) {
            const int k = y*width + x;

            const BOOL isSeam = (self.showSeams && _seamGraph.nodes[k].maskLevel > 0);

            rgbData[k*4] = 0;

            rgbData[k*4+1] = (isSeam ?   0 : bPlane[y][x]);
            rgbData[k*4+2] = (isSeam ?   0 : gPlane[y][x]);
            rgbData[k*4+3] = (isSeam ? 255 : rPlane[y][x]);
        }
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(rgbData, width, height, 8, width*sizeof(unsigned int), colorSpace, kCGBitmapByteOrder32Little|kCGImageAlphaNoneSkipLast);
	CGImageRef cgimage = CGBitmapContextCreateImage(context);

	CGContextRelease(context);
	CGColorSpaceRelease(colorSpace);

    NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgimage];
	CGImageRelease(cgimage);
    free(rgbData);

    SMImage_Free(workingImage);
    
    _dirtyImage = NO;

    return imageRep;
}


- (void)viewWillDraw
{
    if (_image.nPlanes == 0)
        return;

    // Recalculate image for live seam carving only if seams
    //  are not being displayed.
    if ([self inLiveResize] && self.showSeams == NO)
        _dirtyImage = YES;

    if (_dirtyGraph) {
        [self updateSeamGraph];
        // Update image to reflect changed seam graph
        _dirtyImage = YES;
    }    
}

- (void)drawRect:(NSRect)rect
{
    static NSImageRep *imageRep = nil;

    if (_dirtyImage == YES || imageRep == nil)
        imageRep = [self bitmapRepForSize:rect.size rect:rect];

    if (imageRep == nil) {
        [[NSColor whiteColor] set];
        NSRectFill(rect);
        return;
    }

    [imageRep drawInRect:rect];

/*
    NSImage *im = [[NSImage alloc] init];
    [im addRepresentation:imageRep];
    [[im TIFFRepresentation] writeToFile:@"/Users/osmerc/Desktop/IP_Project/Images/output.tiff" atomically:NO];
 */
}

@end
